-- Copyright (c) 2010, Pavel Kovar
-- All rights reserved.
--
---------------------------------------------------------------------------------------
-- This file is a part of the Witch Navigator project

-- I2C controller core
-- Implemented  
--   * Master I2C controller 
--   * Single register write transaction
--   * Single register read transaction
--   * Block transactions are not implemented! 

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
library UNISIM;
use UNISIM.VComponents.all;

entity i2c_core is
    Port ( clk : in std_logic;
			  clk_en : in std_logic;
			  sda_in : IN std_logic;
           sda_out : out  STD_LOGIC;
           scl_out : out  STD_LOGIC;
			  i2c_control : in std_logic_vector(31 downto 0);
			  i2c_status : out std_logic_vector(31 downto 0)
			  );
end i2c_core;

architecture Behavioral of i2c_core is

type i2c_state_type is (idl, error_st,
								-- start states
								start1, start2, start3,
								-- device addres states
								da7_1, da7_2, da7_3,
								da6_1, da6_2, da6_3,
								da5_1, da5_2, da5_3,
								da4_1, da4_2, da4_3,
								da3_1, da3_2, da3_3,
								da2_1, da2_2, da2_3,								
								da1_1, da1_2, da1_3,
								da0_1, da0_2, da0_3,
								ackda_1, ackda_2, ackda_3,
								-- register addres states
								ra7_1, ra7_2, ra7_3,
								ra6_1, ra6_2, ra6_3,
								ra5_1, ra5_2, ra5_3,
								ra4_1, ra4_2, ra4_3,
								ra3_1, ra3_2, ra3_3,
								ra2_1, ra2_2, ra2_3,
								ra1_1, ra1_2, ra1_3,
								ra0_1, ra0_2, ra0_3,
								ackra_1, ackra_2, ackra_3,
								-- write register states
								dd7_1, dd7_2, dd7_3,
								dd6_1, dd6_2, dd6_3,
								dd5_1, dd5_2, dd5_3,
								dd4_1, dd4_2, dd4_3,
								dd3_1, dd3_2, dd3_3,
								dd2_1, dd2_2, dd2_3,
								dd1_1, dd1_2, dd1_3,
								dd0_1, dd0_2, dd0_3,
								ackdd_1, ackdd_2, ackdd_3,
								-- stop states for write opperation
								stop_1, stop_2,
								
								-- start states for read opperatoion
								restart_1, restart_2,
								-- device addres for read opperation states
								dar7_1, dar7_2, dar7_3,
								dar6_1, dar6_2, dar6_3,
								dar5_1, dar5_2, dar5_3,
								dar4_1, dar4_2, dar4_3,
								dar3_1, dar3_2, dar3_3,
								dar2_1, dar2_2, dar2_3,								
								dar1_1, dar1_2, dar1_3,
								dar0_1, dar0_2, dar0_3,
								ackdar_1, ackdar_2, ackdar_3,
								-- register read states
								rr7_1, rr7_2, rr7_3,
								rr6_1, rr6_2, rr6_3,
								rr5_1, rr5_2, rr5_3,
								rr4_1, rr4_2, rr4_3,
								rr3_1, rr3_2, rr3_3,
								rr2_1, rr2_2, rr2_3,
								rr1_1, rr1_2, rr1_3,
								rr0_1, rr0_2, rr0_3,
								ackrr_1, ackrr_2, ackrr_3
							  );

signal i2c_state: i2c_state_type;
signal i2c_busy, i2c_error: std_logic;
signal i2c_start, i2c_start_old, i2c_wr: std_logic;
signal sda_oreg, scl_oreg: std_logic;
signal read_reg: std_logic_vector(7 downto 0);

begin
i2c_start <= i2c_control(24);
i2c_wr <= i2c_control(25);

i2c_status(7 downto 0) <= read_reg;
i2c_status(8) <= i2c_busy;
i2c_status(9) <= i2c_error;
i2c_status(31 downto 10) <= "0000000000000000000000";

sda_out <= sda_oreg;
scl_out <= scl_oreg;

process (clk)
begin
	if clk'event and clk = '1' then
		i2c_start_old <= i2c_start;
		if i2c_start_old = '0' and i2c_start = '1' then
			i2c_busy <= '1';
			i2c_error <= '0';
			i2c_state <= start1;
		elsif clk_en = '1' then	
			case i2c_state is
				when idl =>
					i2c_busy <= '0';
					i2c_error <= '0';
					sda_oreg <= '1';
					scl_oreg <= '1';

				when error_st =>
					i2c_busy <= '0';
					i2c_error <= '1';
					sda_oreg <= '1';
					scl_oreg <= '1';

				when start1 =>
					sda_oreg <= '1';
					scl_oreg <= '1';
					i2c_state <= start2;
				when start2 =>	
					sda_oreg <= '0';
					scl_oreg <= '1';
					i2c_state <= start3;
				when start3 =>	
					sda_oreg <= '0';
					scl_oreg <= '0';
					i2c_state <= da7_1;
-- device addres
				when da7_1 =>
					sda_oreg <= i2c_control(23);
					scl_oreg <= '0';
					i2c_state <= da7_2;
				when da7_2 =>
					scl_oreg <= '1';
					i2c_state <= da7_3;
				when da7_3 =>
					scl_oreg <= '0';
					i2c_state <= da6_1;

				when da6_1 =>
					sda_oreg <= i2c_control(22);
					scl_oreg <= '0';
					i2c_state <= da6_2;
				when da6_2 =>
					scl_oreg <= '1';
					i2c_state <= da6_3;
				when da6_3 =>
					scl_oreg <= '0';
					i2c_state <= da5_1;

				when da5_1 =>
					sda_oreg <= i2c_control(21);
					scl_oreg <= '0';
					i2c_state <= da5_2;
				when da5_2 =>
					scl_oreg <= '1';
					i2c_state <= da5_3;
				when da5_3 =>
					scl_oreg <= '0';
					i2c_state <= da4_1;

				when da4_1 =>
					sda_oreg <= i2c_control(20);
					scl_oreg <= '0';
					i2c_state <= da4_2;
				when da4_2 =>
					scl_oreg <= '1';
					i2c_state <= da4_3;
				when da4_3 =>
					scl_oreg <= '0';
					i2c_state <= da3_1;
				
				when da3_1 =>
					sda_oreg <= i2c_control(19);
					scl_oreg <= '0';
					i2c_state <= da3_2;
				when da3_2 =>
					scl_oreg <= '1';
					i2c_state <= da3_3;
				when da3_3 =>
					scl_oreg <= '0';
					i2c_state <= da2_1;
				
				when da2_1 =>
					sda_oreg <= i2c_control(18);
					scl_oreg <= '0';
					i2c_state <= da2_2;
				when da2_2 =>
					scl_oreg <= '1';
					i2c_state <= da2_3;
				when da2_3 =>
					scl_oreg <= '0';
					i2c_state <= da1_1;

				when da1_1 =>
					sda_oreg <= i2c_control(17);
					scl_oreg <= '0';
					i2c_state <= da1_2;
				when da1_2 =>
					scl_oreg <= '1';
					i2c_state <= da1_3;
				when da1_3 =>
					scl_oreg <= '0';
					i2c_state <= da0_1;

				when da0_1 =>
					sda_oreg <= '0';-- WR
					scl_oreg <= '0';
					i2c_state <= da0_2;
				when da0_2 =>
					scl_oreg <= '1';
					i2c_state <= da0_3;
				when da0_3 =>
					scl_oreg <= '0';
					i2c_state <= ackda_1;

				when ackda_1 =>
					sda_oreg <= '1';
					scl_oreg <= '0';
					i2c_state <= ackda_2;
				when ackda_2 =>	
					scl_oreg <= '1';
					i2c_state <= ackda_3;
				when ackda_3 =>	
					if sda_in = '0' then
						i2c_state <= ra7_1;
					else
						i2c_state <= error_st;
						i2c_error <= '1';
						i2c_busy <= '0';
					end if;
					scl_oreg <= '0';
-- register addres					
				when ra7_1 =>
					sda_oreg <= i2c_control(15);
					scl_oreg <= '0';
					i2c_state <= ra7_2;
				when ra7_2 =>
					scl_oreg <= '1';
					i2c_state <= ra7_3;
				when ra7_3 =>
					scl_oreg <= '0';
					i2c_state <= ra6_1;

				when ra6_1 =>
					sda_oreg <= i2c_control(14);
					scl_oreg <= '0';
					i2c_state <= ra6_2;
				when ra6_2 =>
					scl_oreg <= '1';
					i2c_state <= ra6_3;
				when ra6_3 =>
					scl_oreg <= '0';
					i2c_state <= ra5_1;

				when ra5_1 =>
					sda_oreg <= i2c_control(13);
					scl_oreg <= '0';
					i2c_state <= ra5_2;
				when ra5_2 =>
					scl_oreg <= '1';
					i2c_state <= ra5_3;
				when ra5_3 =>
					scl_oreg <= '0';
					i2c_state <= ra4_1;

				when ra4_1 =>
					sda_oreg <= i2c_control(12);
					scl_oreg <= '0';
					i2c_state <= ra4_2;
				when ra4_2 =>
					scl_oreg <= '1';
					i2c_state <= ra4_3;
				when ra4_3 =>
					scl_oreg <= '0';
					i2c_state <= ra3_1;

				when ra3_1 =>
					sda_oreg <= i2c_control(11);
					scl_oreg <= '0';
					i2c_state <= ra3_2;
				when ra3_2 =>
					scl_oreg <= '1';
					i2c_state <= ra3_3;
				when ra3_3 =>
					scl_oreg <= '0';
					i2c_state <= ra2_1;

				when ra2_1 =>
					sda_oreg <= i2c_control(10);
					scl_oreg <= '0';
					i2c_state <= ra2_2;
				when ra2_2 =>
					scl_oreg <= '1';
					i2c_state <= ra2_3;
				when ra2_3 =>
					scl_oreg <= '0';
					i2c_state <= ra1_1;

				when ra1_1 =>
					sda_oreg <= i2c_control(9);
					scl_oreg <= '0';
					i2c_state <= ra1_2;
				when ra1_2 =>
					scl_oreg <= '1';
					i2c_state <= ra1_3;
				when ra1_3 =>
					scl_oreg <= '0';
					i2c_state <= ra0_1;

				when ra0_1 =>
					sda_oreg <= i2c_control(8);
					scl_oreg <= '0';
					i2c_state <= ra0_2;
				when ra0_2 =>
					scl_oreg <= '1';
					i2c_state <= ra0_3;
				when ra0_3 =>
					scl_oreg <= '0';
					i2c_state <= ackra_1;

				when ackra_1 =>
					sda_oreg <= '1';
					scl_oreg <= '0';
					i2c_state <= ackra_2;
				when ackra_2 =>	
					scl_oreg <= '1';
					i2c_state <= ackra_3;
				when ackra_3 =>	
					if sda_in = '0' then
						if i2c_wr = '1' then
							i2c_state <= dd7_1; -- write to register
						else
							i2c_state <= restart_1; -- read register
						end if;
					else
						i2c_state <= error_st;
						i2c_error <= '1';
						i2c_busy <= '0';
					end if;
					scl_oreg <= '0';
-- write data to register
				when dd7_1 =>
					sda_oreg <= i2c_control(7);
					scl_oreg <= '0';
					i2c_state <= dd7_2;
				when dd7_2 =>
					scl_oreg <= '1';
					i2c_state <= dd7_3;
				when dd7_3 =>
					scl_oreg <= '0';
					i2c_state <= dd6_1;

				when dd6_1 =>
					sda_oreg <= i2c_control(6);
					scl_oreg <= '0';
					i2c_state <= dd6_2;
				when dd6_2 =>
					scl_oreg <= '1';
					i2c_state <= dd6_3;
				when dd6_3 =>
					scl_oreg <= '0';
					i2c_state <= dd5_1;

				when dd5_1 =>
					sda_oreg <= i2c_control(5);
					scl_oreg <= '0';
					i2c_state <= dd5_2;
				when dd5_2 =>
					scl_oreg <= '1';
					i2c_state <= dd5_3;
				when dd5_3 =>
					scl_oreg <= '0';
					i2c_state <= dd4_1;

				when dd4_1 =>
					sda_oreg <= i2c_control(4);
					scl_oreg <= '0';
					i2c_state <= dd4_2;
				when dd4_2 =>
					scl_oreg <= '1';
					i2c_state <= dd4_3;
				when dd4_3 =>
					scl_oreg <= '0';
					i2c_state <= dd3_1;

				when dd3_1 =>
					sda_oreg <= i2c_control(3);
					scl_oreg <= '0';
					i2c_state <= dd3_2;
				when dd3_2 =>
					scl_oreg <= '1';
					i2c_state <= dd3_3;
				when dd3_3 =>
					scl_oreg <= '0';
					i2c_state <= dd2_1;
					
				when dd2_1 =>
					sda_oreg <= i2c_control(2);
					scl_oreg <= '0';
					i2c_state <= dd2_2;
				when dd2_2 =>
					scl_oreg <= '1';
					i2c_state <= dd2_3;
				when dd2_3 =>
					scl_oreg <= '0';
					i2c_state <= dd1_1;

				when dd1_1 =>
					sda_oreg <= i2c_control(1);
					scl_oreg <= '0';
					i2c_state <= dd1_2;
				when dd1_2 =>
					scl_oreg <= '1';
					i2c_state <= dd1_3;
				when dd1_3 =>
					scl_oreg <= '0';
					i2c_state <= dd0_1;

				when dd0_1 =>
					sda_oreg <= i2c_control(0);
					scl_oreg <= '0';
					i2c_state <= dd0_2;
				when dd0_2 =>
					scl_oreg <= '1';
					i2c_state <= dd0_3;
				when dd0_3 =>
					scl_oreg <= '0';
					i2c_state <= ackdd_1;

				when ackdd_1 =>
					sda_oreg <= '1';
					scl_oreg <= '0';
					i2c_state <= ackdd_2;
				when ackdd_2 =>	
					scl_oreg <= '1';
					i2c_state <= ackdd_3;
				when ackdd_3 =>	
					if sda_in = '0' then
						i2c_state <= stop_1;
					else
						i2c_state <= error_st;
						i2c_error <= '1';
						i2c_busy <= '0';
					end if;
					scl_oreg <= '0';
-- stop 
				when stop_1 =>
					sda_oreg <= '0';
					scl_oreg <= '0';
					i2c_state <= stop_2;
				when stop_2 =>
					sda_oreg <= '0';
					scl_oreg <= '1';
					i2c_state <= idl;
-- i2c write register completed successfully

-- read from i2c
-- start
				when restart_1 =>
					scl_oreg <= '1';
					i2c_state <= restart_2;
				when restart_2 =>
					sda_oreg <= '0';
					i2c_state <= dar7_1;
-- device addres for read opperation 
				when dar7_1 =>
					sda_oreg <= i2c_control(23);
					scl_oreg <= '0';
					i2c_state <= dar7_2;
				when dar7_2 =>
					scl_oreg <= '1';
					i2c_state <= dar7_3;
				when dar7_3 =>
					scl_oreg <= '0';
					i2c_state <= dar6_1;

				when dar6_1 =>
					sda_oreg <= i2c_control(22);
					scl_oreg <= '0';
					i2c_state <= dar6_2;
				when dar6_2 =>
					scl_oreg <= '1';
					i2c_state <= dar6_3;
				when dar6_3 =>
					scl_oreg <= '0';
					i2c_state <= dar5_1;

				when dar5_1 =>
					sda_oreg <= i2c_control(21);
					scl_oreg <= '0';
					i2c_state <= dar5_2;
				when dar5_2 =>
					scl_oreg <= '1';
					i2c_state <= dar5_3;
				when dar5_3 =>
					scl_oreg <= '0';
					i2c_state <= dar4_1;

				when dar4_1 =>
					sda_oreg <= i2c_control(20);
					scl_oreg <= '0';
					i2c_state <= dar4_2;
				when dar4_2 =>
					scl_oreg <= '1';
					i2c_state <= dar4_3;
				when dar4_3 =>
					scl_oreg <= '0';
					i2c_state <= dar3_1;
				
				when dar3_1 =>
					sda_oreg <= i2c_control(19);
					scl_oreg <= '0';
					i2c_state <= dar3_2;
				when dar3_2 =>
					scl_oreg <= '1';
					i2c_state <= dar3_3;
				when dar3_3 =>
					scl_oreg <= '0';
					i2c_state <= dar2_1;
				
				when dar2_1 =>
					sda_oreg <= i2c_control(18);
					scl_oreg <= '0';
					i2c_state <= dar2_2;
				when dar2_2 =>
					scl_oreg <= '1';
					i2c_state <= dar2_3;
				when dar2_3 =>
					scl_oreg <= '0';
					i2c_state <= dar1_1;

				when dar1_1 =>
					sda_oreg <= i2c_control(17);
					scl_oreg <= '0';
					i2c_state <= dar1_2;
				when dar1_2 =>
					scl_oreg <= '1';
					i2c_state <= dar1_3;
				when dar1_3 =>
					scl_oreg <= '0';
					i2c_state <= dar0_1;

				when dar0_1 =>
					sda_oreg <= '1';	-- R 
					scl_oreg <= '0';
					i2c_state <= dar0_2;
				when dar0_2 =>
					scl_oreg <= '1';
					i2c_state <= dar0_3;
				when dar0_3 =>
					scl_oreg <= '0';
					i2c_state <= ackdar_1;

				when ackdar_1 =>
					sda_oreg <= '1';
					scl_oreg <= '0';
					i2c_state <= ackdar_2;
				when ackdar_2 => 	
					scl_oreg <= '1';
					i2c_state <= ackdar_3;
				when ackdar_3 =>	
					if sda_in = '0' then
						i2c_state <= rr7_1;
					else
						i2c_state <= error_st;
						i2c_error <= '1';
						i2c_busy <= '0';
					end if;
					scl_oreg <= '0';
-- read i2c reg							
				when rr7_1 =>
					sda_oreg <= '1';
					scl_oreg <= '0';
					i2c_state <= rr7_2;
				when rr7_2 =>
					scl_oreg <= '1';
					i2c_state <= rr7_3;
				when rr7_3 =>
					read_reg(7) <= sda_in;
					scl_oreg <= '0';
					i2c_state <= rr6_1;
					
				when rr6_1 =>
					scl_oreg <= '0';
					i2c_state <= rr6_2;
				when rr6_2 =>
					scl_oreg <= '1';
					i2c_state <= rr6_3;
				when rr6_3 =>
					read_reg(6) <= sda_in;
					scl_oreg <= '0';
					i2c_state <= rr5_1;

				when rr5_1 =>
					scl_oreg <= '0';
					i2c_state <= rr5_2;
				when rr5_2 =>
					scl_oreg <= '1';
					i2c_state <= rr5_3;
				when rr5_3 =>
					read_reg(5) <= sda_in;
					scl_oreg <= '0';
					i2c_state <= rr4_1;

				when rr4_1 =>
					scl_oreg <= '0';
					i2c_state <= rr4_2;
				when rr4_2 =>
					scl_oreg <= '1';
					i2c_state <= rr4_3;
				when rr4_3 =>
					read_reg(4) <= sda_in;
					scl_oreg <= '0';
					i2c_state <= rr3_1;

				when rr3_1 =>
					scl_oreg <= '0';
					i2c_state <= rr3_2;
				when rr3_2 =>
					scl_oreg <= '1';
					i2c_state <= rr3_3;
				when rr3_3 =>
					read_reg(3) <= sda_in;
					scl_oreg <= '0';
					i2c_state <= rr2_1;

				when rr2_1 =>
					scl_oreg <= '0';
					i2c_state <= rr2_2;
				when rr2_2 =>
					scl_oreg <= '1';
					i2c_state <= rr2_3;
				when rr2_3 =>
					read_reg(2) <= sda_in;
					scl_oreg <= '0';
					i2c_state <= rr1_1;

				when rr1_1 =>
					scl_oreg <= '0';
					i2c_state <= rr1_2;
				when rr1_2 =>
					scl_oreg <= '1';
					i2c_state <= rr1_3;
				when rr1_3 =>
					read_reg(1) <= sda_in;
					scl_oreg <= '0';
					i2c_state <= rr0_1;

				when rr0_1 =>
					scl_oreg <= '0';
					i2c_state <= rr0_2;
				when rr0_2 =>
					scl_oreg <= '1';
					i2c_state <= rr0_3;
				when rr0_3 =>
					read_reg(0) <= sda_in;
					scl_oreg <= '0';
					i2c_state <= ackrr_1;

				when ackrr_1 =>			-- ack generated by master
					sda_oreg <= '0';
					scl_oreg <= '0';
					i2c_state <= ackrr_2;
				when ackrr_2 =>	
					scl_oreg <= '1';
					i2c_state <= ackrr_3;
				when ackrr_3 =>	
					i2c_state <= stop_1;
					scl_oreg <= '0';
				when others =>
					i2c_state <= idl;
			end case;
		end if;	
	end if;
end process;

end Behavioral;

